<?php

namespace App\Service\VendorClient\OrderPayDeclareService\WechatPayDeclare;

use App\Model\PssModel\OrderPayDeclare as OrderDeclare;
use Service\AppService\IdService;
use App\Service\Code\Normal;
use App\Service\OrderService\OrderInterfaceService\OrderPayDeclare;
use App\Service\TaskServer\TaskWorker;
use Service\BaseService;

/**
 * 微信支付单申报服务
 */
class WechatPayDeclareApiClient extends BaseService
{
    protected $clientConfig;
    
    //推送微信支付单申报接口地址
    protected $router = [
        'orderPayDeclare'       => 'https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclareorder',
        'orderPayDeclareQuery'  => 'https://api.mch.weixin.qq.com/cgi-bin/mch/customs/customdeclarequery',
        'orderPayReDeclare'     => 'https://api.mch.weixin.qq.com/cgi-bin/mch/newcustoms/customdeclareredeclare',
    ];
    
    /**
     * 初始参数
     */
    public function __construct()
    {
        $this->clientConfig = config('api.wechatPayDeclare');
    }

    /**
     * 申报数据
     * @param $params
     */
    public function payDeclare($params)
    {
        if (empty($params['appid'])) {
            return Normal::returnCode(Normal::FAIL, 'app_id为空');
        }
        if (empty($params['mch_id'])) {
            return Normal::returnCode(Normal::FAIL, 'mch_id为空');
        }
        if (empty($params['api_key'])) {
            return Normal::returnCode(Normal::FAIL, 'api_key为空');
        }
        $supplierOrdersRequest = [];
        $insertData = [];
        $current_timestamp = time();
        $old_orders_no = $params['sub_order_no'];
        $old_orders = OrderDeclare::instance()->where('sub_order_no', $old_orders_no)->first();
        //不存在的数据才去调用申报接口
        if ($old_orders) {
            $params['sub_order_id'] = $old_orders['sub_order_id'];
            return $this->payDeclareQuery($params);
        }
        $result = $this->checkData($params);
        if ($result['code'] != 200) {
            return $result;
        }
        
        $request_data = [
            'appid'             => $params['appid'],
            'mch_id'            => $params['mch_id'],
            'sign_type'         => 'MD5',
            'out_trade_no'      => $params['out_trade_no'],
            'transaction_id'    => $params['transaction_id'],
            'customs'           => $params['customs'],
            'mch_customs_no'    => $params['mch_customs_no'],
            'cert_type'         => $params['cert_type'],
            'cert_id'           => $params['cert_id'],
            'name'              => $params['name'],
        ];

        if (isset($params['sub_order_no'])) {
            $request_data['sub_order_no']   = $params['sub_order_no'];
            $request_data['fee_type']       = $params['fee_type'];
            $request_data['order_fee']      = $params['order_fee'];
            if ($params['transport_fee'] > 0) {
                $request_data['transport_fee'] = $params['transport_fee'];
            }
            $request_data['product_fee'] = $params['product_fee'];
        }

        $request_data['sign'] = $this->makeSign($request_data, $params['api_key']);
        $supplierOrdersRequest = [
            [
                'tag'       => 'wechatPayDeclare_orderPayDeclare',
                'uid'       => IdService::instance()->getOtherID(),
                'url'       => $this->router['orderPayDeclare'],
                'data'      => $this->arrayToXml($request_data),
                'method'    => 'post',
                'format'    => 'x-www-form-urlencoded',
                'header'    => [],
            ]
        ];
        $insertData = [
            'bid'               => $params['bid'],
            'order_type'        => $params['orderPayType'],
            'out_trade_no'      => $params['out_trade_no'],
            'transaction_id'    => $params['transaction_id'],
            'sub_order_no'      => $params['sub_order_no'],
            'customs'           => $params['customs'],
            'mch_customs_no'    => $params['mch_customs_no'],
            'status'            => 0,
            'contents'          => json_encode($request_data, JSON_UNESCAPED_UNICODE),
            'cd'                => $current_timestamp,
            'md'                => $current_timestamp
        ];
        
        //保存数据
        $resTask = TaskWorker::instance()->addTask($supplierOrdersRequest);
        $resTask = current($resTask);
        $resItem = $this->xmlToArray($resTask['response']['result']);
        $insertData['response'] = json_encode($resItem, JSON_UNESCAPED_UNICODE);
        if ($resItem['return_code'] == "FAIL" || $resItem['result_code'] == 'FAIL') {
            $insertData['status'] = 2;
            return Normal::returnCode(Normal::FAIL, "微信支付单申报失败【{$insertData['response']}】");
        } else {
            $insertData['sub_order_id'] = $resItem['sub_order_id'];
            \App\Model\PssModel\OrderPayDeclare::instance()->insert($insertData);
            return Normal::returnCode(Normal::SUC, "微信支付单提交成功", $resItem);
        }
    }


    /**
     * 查询申报数据接口
     * @param $params
     */
    public function payDeclareQuery($params)
    {
        if (empty($params['appid'])) {
            return Normal::returnCode(Normal::FAIL, 'app_id为空');
        }
        if (empty($params['mch_id'])) {
            return Normal::returnCode(Normal::FAIL, 'mch_id为空');
        }
        if (empty($params['api_key'])) {
            return Normal::returnCode(Normal::FAIL, 'api_key为空');
        }
        if (empty($params['sub_order_no'])) {
            return Normal::returnCode(Normal::FAIL, 'sub_order_no为空');
        }
        if (empty($params['customs'])) {
            return Normal::returnCode(Normal::FAIL, 'customs为空');
        }

        $data = [
            'appid'             => $params['appid'],
            'mch_id'            => $params['mch_id'],
            'sub_order_id'      => $params['sub_order_id'],
            'customs'           => $params['customs'],
//            'sub_order_no' => $params['sub_order_no'],
//            'transaction_id' => $params['transaction_id'],
//            'out_trade_no' => $params['out_trade_no'],
            'sign_type'         => 'MD5',
        ];

        $response = $this->execute($data, 'orderPayDeclareQuery', $params['api_key']);
        if ($response['code'] == Normal::SUC) 
        {
            //查找key
            $key        = array_search($params['sub_order_id'], $response['data']);
            $array      = explode('_', $key);
            $state_key  = end($array);
            if ($response['data']['state_' . $state_key] == 'SUCCESS') {
                OrderDeclare::instance()->where('sub_order_no', $params['sub_order_no'])->update(['status' => 1]);
            } elseif ($response['data']['state_' . $state_key] == 'FAIL' || $response['data']['state_' . $state_key] == 'EXCEPT') {
                OrderDeclare::instance()->where('sub_order_no', $params['sub_order_no'])->update(['status' => 2]);
            }
            //这边拼装数据
            $result = [
                'return_code'=>$response['data']['return_code'],
                'count' => $response['data']['count'],
                'sub_order_id' => $response['data']['sub_order_id_' . $state_key],
                'sub_order_no' => $response['data']['sub_order_no_' . $state_key],
                'state' => $response['data']['state_' . $state_key],
                'out_trade_no' =>$params['out_trade_no'],
            ];
            return Normal::returnCode(Normal::SUC,'获取申报结果成功',$result);
        }
        return $response;
    }

    /**
     * 重新推送支付单
     * @param $params
     */
    public function orderPayReDeclare($params)
    {
        if (empty($params['app_id'])) {
            return Normal::returnCode(Normal::FAIL, 'app_id为空');
        }
        if (empty($params['mch_id'])) {
            return Normal::returnCode(Normal::FAIL, 'mch_id为空');
        }
        if (empty($params['api_key'])) {
            return Normal::returnCode(Normal::FAIL, 'api_key为空');
        }
        if (empty($params['out_trade_no'])) {
            return Normal::returnCode(Normal::FAIL, 'out_trade_no为空');
        }
        if (empty($params['sub_order_no'])) {
            return Normal::returnCode(Normal::FAIL, 'sub_order_no为空');
        }
        if (empty($params['customs'])) {
            return Normal::returnCode(Normal::FAIL, 'customs为空');
        }
        if (empty($params['mch_customs_no'])) {
            return Normal::returnCode(Normal::FAIL, 'mch_customs_no为空');
        }
        $data = [
            'appid'             => $params['app_id'],
            'mch_id'            => $params['mch_id'],
            'out_trade_no'      => $params['out_trade_no'],
            'sub_order_no'      => $params['sub_order_no'],
            'customs'           => $params['customs'],
            'mch_customs_no'    => $params['mch_customs_no'],
        ];
        return $this->execute($data, 'orderPayReDeclare', $params['api_key']);
    }

    /**
     * 修改支付单信息
     * @param $params
     */
    public function editPayInfo($params)
    {
        if (empty($params['app_id'])) {
            return Normal::returnCode(Normal::FAIL, 'app_id为空');
        }
        if (empty($params['mch_id'])) {
            return Normal::returnCode(Normal::FAIL, 'mch_id为空');
        }
        if (empty($params['api_key'])) {
            return Normal::returnCode(Normal::FAIL, 'api_key为空');
        }
        $result = $this->checkData($params['supplierOrder']);
        if ($result['code'] != 200) {
            return $result;
        }
        $order = $params['supplierOrder'];
        $updateData = [
            'bid'               => $params['bid'],
            'out_trade_no'      => $order['out_trade_no'],
            'transaction_id'    => $order['transaction_id'],
            'sub_order_no'      => $order['sub_order_no'],
            'customs'           => $order['customs'],
            'mch_customs_no'    => $order['mch_customs_no'],
            'contents'          => json_encode($order, JSON_UNESCAPED_UNICODE),
            'md'                => time()
        ];
        $order['appid']         = $params['app_id'];
        $order['mch_id']        = $params['mch_id'];
        $order['sign_type']     = 'MD5';
        OrderDeclare::instance()->where('out_trade_no', $order['out_trade_no'])->update($updateData);
        return $this->execute($order, 'editPayInfo', $params['api_key']);
    }

    /**
     * 校验数据
     * @param $data
     * @return array
     */
    public function checkData($data)
    {
        $rule = [
            //商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*@ ，且在同一个商户号下唯一。
            'out_trade_no' => 'require',
            //微信支付返回的订单号
            'transaction_id' => 'require',
            /**
             * GUANGZHOU_ZS 广州（总署版）
             * HANGZHOU_ZS 杭州（总署版）
             * NINGBO 宁波
             * ZHENGZHOU_BS 郑州（保税物流中心）
             * CHONGQING 重庆
             * SHANGHAI_ZS 上海（总署版）
             * SHENZHEN 深圳
             * ZHENGZHOU_ZH_ZS 郑州综保（总署版）
             * TIANJIN 天津（需要推送订单至天津海关时，需要在商户管理后台同时配置天津海关备案信息与天津国检备案信息；调用报关接口时只需推送天津海关，即请求一次报关接口。）
             * 接口文档列表没有的海关，商户在商户平台新增海关备案信息时选“广州（总署）”，备案号和备案名称填商户在海关登记的信息，然后按商户平台登记的信息调用“订单附加信息提交接口”即可
             */
            'customs' => 'require',
            //商户在海关登记的备案号，customs非NO，此参数必填
            'mch_customs_no' => 'require',
            //关税(非必传)
            // 'duty' => 'require',
            /**
             *
             * 报关类型
             * 不传，默认是新增
             * ADD 新增报关申请
             * MODIFY 修改
             * 报关信息
             */
//            'action_type' => 'require',
            // 证件类型	 请传固定值IDCARD,暂只支持大陆身份证。
            'cert_type' => 'require',
            //证件号码	用户大陆身份证号，尾号为字母X的身份证号，请大写字母X。
            'cert_id' => 'require',
            //姓名 用户姓名。
            'name' => 'require',
            'sub_order_no' => 'require',
            'fee_type' => 'require',
            'order_fee' => 'require',
            'transport_fee' => 'require',
            'product_fee' => 'require',
        ];


        /**
         *   拆单或拆单修改时
         *     [
         *          'sub_order_no'=>'', 商户子订单号
         *          'fee_type'=>'',币种
         *          'order_fee'=>'',应付金额
         *          'transport_fee'=>'',物流费
         *          'product_fee'=>'',商品价格
         *      ]
         */

        $check_res = $this->check($data, $rule);
        if ($check_res['code'] != 200) {
            return Normal::returnCode(Normal::FAIL, $check_res['msg']);
        }
        return Normal::returnCode(Normal::SUC, '验证成功');
    }

    /**
     * 检查数据
     * @param $params
     * @param $rule
     * @return array
     */
    public function check($params, $rule)
    {
        foreach ($rule as $k => $r) {
            if (!isset($params[$k])) {
                return Normal::returnCode(Normal::FAIL, '缺少' . $k);
            }
        }
        return Normal::returnCode(Normal::SUC, '验证成功');
    }

    /**
     * 签名
     * @param $data
     */
    private function makeSign($data, $key)
    {
        ksort($data);
        $string = $this->toUrlParams($data);
        return strtoupper(md5($string . "&key=" . $key));
    }

    /**
     * 统一请求
     * @param $data
     * @param $method
     * @param $api_key
     * @return array
     */
    private function execute($data, $method, $api_key)
    {
        $data['sign'] = $this->makeSign($data, $api_key);
        $parameter = [
            [
                'tag'       => 'wechatPayDeclare_' . $method,
                'uid'       => IdService::instance()->getOtherID(),
                'url'       => $this->router[$method],
                'data'      => $this->arrayToXml($data),
                'method'    => 'post',
                'format'    => 'form-data',
                'header'    => [],
            ],
        ];
        $resTask = TaskWorker::instance()->addTask($parameter);
        $resTask = current($resTask);
        $resHttp = $this->xmlToArray($resTask['response']['result']);
        $errorMsg = $resHttp['return_msg'] ?? '请求失败';
        if ($resHttp['return_code'] == 'SUCCESS' && $resHttp['result_code'] == 'SUCCESS') {
            return Normal::returnCode(Normal::SUC, "操作成功", $resHttp);
        }
        return Normal::returnCode(Normal::FAIL, $errorMsg);
    }

    /**
     * 拼接路由
     * @param $data
     * @return string
     */
    private function toUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     *   将数组转换为xml
     * @param array $data 要转换的数组
     * @param bool $root 是否要根节点
     * @return string        xml字符串
     */
    private function arrayToXml($data, $root = true)
    {
        $str = "";
        if ($root) {
            $str .= "<xml>";
        }
        foreach ($data as $key => $val) {
            if (is_array($val)) {
                $child = $this->arrayToXml($val, false);
                $str .= "<$key>$child</$key>";
            } else {
                $str .= "<$key>$val</$key>";
            }

        }
        if ($root) {
            $str .= "</xml>";
        }
        return $str;
    }

    /**
     * XML转数组
     * @param $xml
     */
    private function xmlToArray($xml)
    {
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }
}